using System;
using System.Xml;
using System.Collections.Generic;

namespace tvgrabau
{
	public class Config
	{
		private string _filename;
		private bool locked = false;
		public bool IgnoreErrors = false;
		
		public string Filename
		{
			get
			{
				return _filename;
			}
			set
			{
				_filename = value;
				locked = false;
			}
		}
		
		// Class init
		public Config(string f)
		{
			// Setup our private filename variable..
			_filename = f;
			// Unlock from previous errors
			locked = false;
		}
		
		public Config()
		{
			locked = true;
		}
		
		// We encountered an error, and by default, you can't unlock it..
		public void unlock()
		{
			locked = false;
		}
	
		// Loads the config and the associated variables
		public GrabberOptions loadConfiguration()
		{
			bool QuietNotices = IgnoreErrors;
			GrabberOptions go = new GrabberOptions();
			try
			{
				if (locked == false)
				{
					XmlDocument docXML = new XmlDocument();
					docXML.Load(_filename);
				
					XmlNode rootNode = docXML.DocumentElement;
					// Get all the children of the configuration..
					XmlNodeList children = rootNode.ChildNodes;
					
					// Runs through each Configuration section
					// Grabber, proxy and ChannelMap..
					foreach(XmlNode node in children)
					{
						// Find out where the hell we are..
						if(node.LocalName == "channelmap")
						{
							// Load up the Channelmap into 'ChannelMapList'..
							foreach (XmlNode n in node)
							{									
								if (n.Attributes.Count == 2)
								{
									go.ChannelMaps.Add(new GrabberOptions.ChannelMap(n.Attributes[0].Value, n.InnerText, n.Attributes[1].Value));
								}
								else 
								{
									go.ChannelMaps.Add(new GrabberOptions.ChannelMap(n.Attributes[0].Value, n.InnerText));
								}
							}
						}
						else if (node.LocalName == "proxy")
						{
							// Find out whether proxy is enabled or not..
							if (node.Attributes.Count > 0)
							{
								// Hooray..! There's something there..
								// Read it..	
								foreach (XmlAttribute att in node.Attributes)
								{
								    if (att.Name == "enabled")
								    {
								        go.Proxy.Enabled = bool.Parse(att.Value);
								    }
								    else if (att.Name == "tor")
								    {
								        go.Proxy.Tor = bool.Parse(att.Value);
								    }
								}
							}
							foreach(XmlNode n in node)
							{
								if (n.LocalName == "username")
								{
									go.Proxy.Username = n.InnerText;
								}
								else if (n.LocalName == "password")
								{
									go.Proxy.Password = n.InnerText;
								}
								else if (n.LocalName == "host")
								{
									go.Proxy.Hostname = n.InnerText;
								}
								else if (n.LocalName == "port")
								{
								    go.Proxy.Port = int.Parse(n.InnerText);
								}
							}
						}
						else if (node.LocalName == "remotehosts")
						{
							string[] remotehosts = new string[node.ChildNodes.Count];
							int x = 0;
							foreach (XmlNode n in node)
							{
								if (n.LocalName == "host")
								{
									string a = n.InnerText;
									if (!a.EndsWith("/"))
									{
										a += "/";
									}
									remotehosts[x] = "http://" + a;
								}
								
								x++;
							}
							
							go.RemoteHosts = remotehosts;
						}
						else if (node.LocalName == "grabber")
						{
							foreach(XmlNode n in node)
							{
								if (n.LocalName == "regions")
								{
									foreach (XmlNode y in n)
									{
										if (y.LocalName == "freetoair")
										{
											go.Region.FreeToAir = y.InnerText;
										}
										else if (y.LocalName == "cable")
										{
											go.Region.Cable = y.InnerText;
										}
									}
								}
								else if (n.LocalName == "venues")
								{
									int capacity = n.ChildNodes.Count;
									string[] v = new string[capacity];
									int x = 0;
									foreach (XmlNode y in n)
									{
										v[x] = y.InnerText;
										x++;
									}
									
									go.VenueIDList = v;
								}
								else if (n.LocalName == "delaysecs")
								{
									go.FetchDelay = int.Parse(n.InnerText);
								}
								else if (n.LocalName == "quiet")
								{
									go.Quiet = true;
								}
								else if (n.LocalName == "plugin")
								{
									go.PluginName = n.InnerText;
								}
								else if (n.LocalName == "nodesc")
								{
								    go.NoDescriptions = true;
								}
								else if (n.LocalName == "cache")
								{
									if (n.Attributes.Count > 0)
									{
										go.CacheEnabled = bool.Parse(n.Attributes[0].InnerText);
									}
									foreach (XmlNode y in n)
									{
										if (y.LocalName == "location")
										{
											string a = y.InnerText;
											if (!a.EndsWith("/"))
											{
												a += "/";
											}
											
											go.CacheLocation = a;
										}
									}
								}
							}
						}
					}
				}
				
			}
			catch (System.Xml.XmlException ex)
			{
			    if (!IgnoreErrors && MainClass.loud)
			    {
    				Console.WriteLine("Malformed configuration file:");
    				Console.WriteLine(ex.Message);
    				go.Clear();
    				locked = true;
    				QuietNotices = true;
				}
			}
			
			catch (System.Exception ex)
			{
			    if (!IgnoreErrors && MainClass.loud)
			    {
    				if (!System.IO.File.Exists(this._filename))
    				{
    					// Nasty way of doing things
    					//System.IO.File.Create(_filename).Close();
    					Console.WriteLine("[ERROR]: You have no configuration file! I'd recommend using --configure.");
    					Environment.Exit(3);
    				}
    				else
    				{
    					Console.WriteLine("Configuration load exception occured (not XML): "+ex.Message);
    					Console.WriteLine("Occured at: "+ex.Source);
    					Console.WriteLine("Stack trace:");
    					Console.WriteLine(ex.StackTrace);
    				}
    				go.Clear();
    				QuietNotices = true;
    				locked = true;
				}
			}
			
			finally
			{
				if (!QuietNotices || !IgnoreErrors)
				{
				    if (MainClass.loud)
				    {
    					if (go.ChannelMaps.Count == 0)
    					{
    						Console.WriteLine("[ERROR]: You must supply a channel map in order to grab listings!");
    						Console.WriteLine("Please edit your config.xml file.");
    						Environment.Exit(3);
    					}
    					else if (go.VenueIDList.Length == 0)
    					{
    						Console.WriteLine("[ERROR]: You must provide at least one venue ID to grab.");
    						Console.WriteLine("Please edit your config.xml file.");
    						Environment.Exit(3);
    					}
    					else if (go.Region.FreeToAir == null || go.Region.FreeToAir == "")
    					{
    						Console.WriteLine("[ERROR]: You must supply a free to air region code for your area!");
    						Console.WriteLine("Please edit your config.xml file.");
    						Environment.Exit(3);
    					}
    					else if (go.RemoteHosts == null || go.RemoteHosts.Length == 0)
    					{
    						Console.WriteLine("[ERROR]: You must have at least one remote host specified.");
    						Console.WriteLine("Please edit your config.xml file.");
    						Environment.Exit(3);
    					}
					}
					else
					{
					    Environment.Exit(3);
					}
				}
			}
			
			return go;
			
		}
		
		public bool writeConfiguration(GrabberOptions go)
		{
			try
			{
				OutputManager.QuickXml.QuickXmlWriter q = new OutputManager.QuickXml.QuickXmlWriter(_filename);
				q.WriteDeclaration();
				
				q.StartTag("configuration", true);	// <configuration>			
				
				if (go.ChannelMaps.Count > 0)
				{
					q.StartTag("channelmap", true);	// <channelmap>
					foreach (GrabberOptions.ChannelMap c in go.ChannelMaps)
					{
						List<OutputManager.QuickXml.QuickXmlAttribute> ac = new List<OutputManager.QuickXml.QuickXmlAttribute>();
						ac.Add(new OutputManager.QuickXml.QuickXmlAttribute("id", c.XMLID));
						if (c.Callsign != null && c.Callsign.Length > 0)
						{
							ac.Add(new OutputManager.QuickXml.QuickXmlAttribute("callsign", c.Callsign));
						}
						
						q.StartTag("channel", ac, "", true);	// <channel>
						q.StartTag("display-name", c.Name);
						q.EndTag();
						q.EndTag();	// </channel>
					}
					
					q.EndTag();						// </channelmap>
				}
				
				if (go.Proxy.Enabled)
				{
					List<OutputManager.QuickXml.QuickXmlAttribute> ac = new List<OutputManager.QuickXml.QuickXmlAttribute>();
					ac.Add(new OutputManager.QuickXml.QuickXmlAttribute("enabled", go.Proxy.Enabled.ToString()));
					ac.Add(new OutputManager.QuickXml.QuickXmlAttribute("tor", go.Proxy.Tor.ToString()));
					q.StartTag("proxy", ac, "", true);	// <proxy>
					
					if (go.Proxy.Username != "")
					{
    					q.StartTag("username", go.Proxy.Username);
	    				q.EndTag();
	    				
	    				if (go.Proxy.Password != "")
	    				{
	    			    	q.StartTag("password", go.Proxy.Password);
        					q.EndTag();
	    				}
	    		    }
	    		    
					q.StartTag("host", go.Proxy.Hostname);
					q.EndTag();
					q.StartTag("port", go.Proxy.Port.ToString());
					q.EndTag();
					
					q.EndTag();							// </proxy>
				}
				
				q.WriteNewLine();
				q.StartTag("grabber", true);	// <grabber>
				
				// Force to use this plugin.
				q.StartTag("plugin", "tvgrabau-yourtv");
				q.EndTag();
				
				q.StartTag("regions", true);	// <regions>
				if (go.Region.FreeToAir != null && go.Region.FreeToAir != "")
				{
					q.StartTag("freetoair", go.Region.FreeToAir);
					q.EndTag();
				}
				if (go.Region.Cable != "0")
				{
					q.StartTag("cable", go.Region.Cable);
					q.EndTag();
				}
				q.EndTag();						// </regions>
				
				q.StartTag("venues", true);		// <venues>
				if (go.VenueIDList != null)
				{
					foreach(string venue in go.VenueIDList)
					{
						q.StartTag("venueid", venue);
						q.EndTag();
					}
				}
				q.EndTag();						// </venues>
				
				if (go.CacheEnabled)
				{
					List<OutputManager.QuickXml.QuickXmlAttribute> ac = new List<OutputManager.QuickXml.QuickXmlAttribute>();
					ac.Add(new OutputManager.QuickXml.QuickXmlAttribute("enabled", go.CacheEnabled.ToString()));
					q.StartTag("cache", ac, "", true);	// <cache>
					q.StartTag("location", go.CacheLocation);
					q.EndTag();
					q.EndTag();							// </cache>
				}
				
				q.StartTag("delaysecs", "6");	// Can only be changed by editing the config.xml file.
				q.EndTag();
				
				if (go.NoDescriptions)
				{
				    q.StartTag("nodesc");       // <nodesc />
				}
				
				q.EndTag();						// </grabber>
				q.WriteNewLine();
				q.StartTag("remotehosts", true);
				
				if (go.RemoteHosts != null)
				{
					foreach(string host in go.RemoteHosts)
					{
						q.StartTag("host", host.Substring(host.IndexOf("://") + 3));
						q.EndTag();
					}
				}
				q.EndTag();
				
				// Write matching </configuration>
				q.EndTag();
				
				q.WriteClose();
			}
			catch (System.Exception ex)
			{
				Console.WriteLine("Configuration write exception occured (not XML): "+ex.Message);
				Console.WriteLine("Occured at: "+ex.Source);
				Console.WriteLine("Stack trace:");
				Console.WriteLine(ex.StackTrace);
				Console.WriteLine("[WARNING]: Failed to write to: {0}", _filename);
				return false;
			}
			
			return true;
		}
		
	}
	
	public class GrabberOptions
	{
		public class _Regions
		{
			public string FreeToAir;
			public string Cable = "0";
		}
		
		public _Regions Region = new _Regions();
		public string[] VenueIDList;
		public int FetchDelay;
		public bool CacheEnabled;
		public string CacheLocation;
		public string PluginName = "";
		public string[] RemoteHosts;		
		
		public bool Quiet = false;
		
		public class _Proxy
		{
			public bool Enabled = false;
			public string Username = "";
			public string Password = "";
			public string Hostname = "";
			public int Port = 8080;
			public bool Tor = false;
		}
		
		public _Proxy Proxy = new _Proxy();
		public bool NoDescriptions = false;
		
		public void Clear()
		{
			Region = new _Regions();
			VenueIDList = null;
			FetchDelay = 10;
			CacheEnabled = false;
			CacheLocation  = String.Empty;
			Proxy = new _Proxy();
			ChannelMaps = new List<ChannelMap>();
			PluginName = String.Empty;
			Quiet = false;
			NoDescriptions = false;
		}
	
		
		public class ChannelMap
		{
			public string XMLID;
			public string Callsign;
			public string Name;
			
			public ChannelMap()
			{
				// Empty init
			}
			
			public ChannelMap(string xmlid)
			{
				XMLID = xmlid;
			}
			
			public ChannelMap(string xmlid, string chname)
			{
				XMLID = xmlid;
				Name = chname;
			}
			
			public ChannelMap(string xmlid, string chname, string callsign)
			{
				XMLID = xmlid;
				Name = chname;
				Callsign = callsign;
			}
		}
		
		public List<ChannelMap> ChannelMaps = new List<ChannelMap>();
	}			
}
